package com.macro.mall.ums.service.login.impl;

import com.macro.mall.security.util.JwtTokenUtil;
import com.macro.mall.ums.entity.MemberEntity;
import com.macro.mall.ums.entity.WxMemberEntity;
import com.macro.mall.ums.service.account.MemberService;
import com.macro.mall.ums.service.account.WxMemberService;
import com.macro.mall.ums.service.login.MemberLoginLogService;
import com.macro.mall.ums.service.login.WxLoginService;
import com.macro.mall.ums.service.login.bo.InsertMemberLoginLogInput;
import com.macro.mall.ums.service.login.bo.LoginByWxMpInput;
import com.macro.mall.ums.service.login.bo.MemberLoginResult;
import com.macro.mall.ums.service.register.RegisterService;
import com.macro.mall.ums.service.register.bo.RegisterByWxMpInput;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * 微信登录服务
 *
 * @author xuyanjun
 * @date 2019-12-09
 */
@Service
public class WxLoginServiceImpl implements WxLoginService {

    @Autowired
    private WxMemberService wxMemberService;

    @Autowired
    private MemberService memberService;

    @Autowired
    private RegisterService registerService;

    @Autowired
    private MemberLoginLogService memberLoginLogService;

    private static final String CLAIM_KEY_USERNAME = "sub";

    private static final String CLAIM_KEY_CREATED = "created";

    @Value("${jwt.secret:}")
    private String secret;

    @Value("${jwt.expiration:}")
    private Long expiration;

    /**
     * 微信公众号应用登录
     *
     * @param input input
     * @return 登录结果
     */
    @Override
    public MemberLoginResult loginByWxMp(LoginByWxMpInput input) {
        MemberLoginResult result = new MemberLoginResult();

        WxMemberEntity wxUser = wxMemberService.getByOpenId(input.getOpenId());
        if (wxUser == null) {
            registerWxMpUser(input);

            wxUser = wxMemberService.getByOpenId(input.getOpenId());

            Assert.notNull(wxUser, "用户未注册成功[10002]。");
            result.setNewUser(true);
        }

        MemberEntity memberEntity = memberService.getById(wxUser.getMemberId());

        InternalLoginInput internalLoginInput = new InternalLoginInput();
        internalLoginInput.setLoginIp(input.getLoginIp())
                .setUserAgent(input.getUserAgent())
                .setMemberId(memberEntity.getId())
                .setUserName(memberEntity.getUsername())
                .setSourceType(input.getUserSourceTypeEnum().getValue());

        loginInternal(internalLoginInput);

        result.setAccessToken(generateToken(memberEntity.getUsername()));

        return result;
    }

    /**
     * 注册微信移动应用用户
     *
     * @param input input
     * @return 用户id
     */
    private long registerWxMpUser(LoginByWxMpInput input) {
        RegisterByWxMpInput registerByWxMpInput = new RegisterByWxMpInput();
        registerByWxMpInput.setOpenId(input.getOpenId())
                .setUnionId(input.getUnionId())
                .setCountry(input.getCountry())
                .setProvince(input.getProvince())
                .setCity(input.getCity())
                .setNickName(input.getNickname())
                .setAvatarUrl(input.getHeadImgUrl())
                .setGender(input.getUserGenderEnum())
                .setRegisterIp(input.getLoginIp())
                .setUserSourceTypeEnum(input.getUserSourceTypeEnum())
                .setBefId(input.getBefId());

        return registerService.registerByWxMp(registerByWxMpInput);
    }

    /**
     * 内部登录
     *
     * @param input input
     */
    private void loginInternal(InternalLoginInput input) {
        InsertMemberLoginLogInput insertUserLoginLogInput = new InsertMemberLoginLogInput();
        insertUserLoginLogInput
                .setMemberId(input.getMemberId())
                .setIp(input.getLoginIp())
                .setSourceType(input.getSourceType())
                .setAgent(input.getUserAgent())
                .setMemberId(input.getMemberId());

        memberLoginLogService.insert(insertUserLoginLogInput);
    }

    /**
     * 根据用户名称生成token
     */
    public String generateToken(String userName) {
        Map<String, Object> claims = new HashMap<>();
        claims.put(CLAIM_KEY_USERNAME, userName);
        claims.put(CLAIM_KEY_CREATED, new Date());
        return generateToken(claims);
    }

    /**
     * 根据负责生成JWT的token
     */
    private String generateToken(Map<String, Object> claims) {
        return Jwts.builder()
                .setClaims(claims)
                .setExpiration(generateExpirationDate())
                .signWith(SignatureAlgorithm.HS512, secret)
                .compact();
    }

    /**
     * 生成token的过期时间
     */
    private Date generateExpirationDate() {
        return new Date(System.currentTimeMillis() + expiration * 1000);
    }


    @Getter
    @Setter
    @Accessors(chain = true)
    private static class InternalLoginInput {
        /**
         * 用户id
         */
        private Long memberId;

        /**
         * 名称
         */
        private String userName;

        /**
         * 用户代理
         */
        private String userAgent;

        /**
         * 登录ip
         */
        private String loginIp;

        /**
         * 来源
         */
        private Integer sourceType;

    }
}
